home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Sound Cards
/
Programming Sound Cards.iso
/
sound_06
/
demo2.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-01
|
27KB
|
1,038 lines
/*
DEMO2.C, Low level demonstration program.
Copyright Ad Lib Inc, 1988.
17-Oct-88, Dale Glowinski, Marc Savary, Ad Lib Inc.
This is a demonstration program which shows an alternate method for
generating sound. Its general strategy is to start a sound, and then
adjust it for volume or frequency at regular intervals. This eliminates
the need to use a music (.ROL) file which would be played as a piece
of music. (However, before coding a sound effect, it is a good idea
to develop an approximation of the desired sound using the
Visual Composer.) This method is only useful for sounds which are
repetitive or continuous.
The program uses several routines in ADLIB.C, which is slightly
different from the original version, but only in that several "static"
statements have been removed to make several lower level routines
available to this module.
Compiled with Microsoft C, V5.0, as follows:
cl -AS -Zi -Ox -Gs demo2.c adlib.obj
Some of the programming was done in a rather arbitrary fashion in order
to save time and to keep the code as simple as possible:
- Volumes are set to a value which was based on whatever sounded best.
Depending on what you wish to do, variables and passing values might
be in order. Ditto for pitches.
- A simple counter was used to create the timing, so that the speed
of the sounds produced by the executable file will vary with the
speed of the processor. Certainly, one would have to use the real
timer in a real application. (The timing counter can be changed by
using the "/t" execution option.)
*/
#include <stdlib.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#include <string.h>
#include "adlib.h"
/* synthesizer modes */
#define MELODIC 0
#define PERCUSSIVE 1
#define MODE MELODIC
/* computer keyboard scan codes */
#define LEFT 75
#define RIGHT 77
#define UP 72
#define DOWN 80
#define PAGEUP 73
#define PAGEDOWN 81
/* flags used for Set_Instrument() */
#define RELEASE 1
#define NO_RELEASE 0
#if (MODE == PERCUSSIVE)
#define NR_VOICES 11
#else
#define NR_VOICES 9
#endif
#define SIREN 0
#define COPTER 1
#define PLANEa 2
#define PLANEb 3
#define BOMBa 4
#define BOMBb 5
#define BOMBc 6
#define BOMBd 7
#define JETa 8
#define JETb 9
#define JETc 10
#define JETd 11
#define JETe 12
#define FAKE_ID 13
#define NR_SOUNDS 14
/* These are declared in ADLIB.C */
extern char slotVoice [][2];
extern char slotPerc [][2];
extern NoteOff (int);
extern NoteOn (int);
/* Timbre information for each sound */
unsigned char op [NR_SOUNDS][2][30];
/* These arrays contain information for each sound */
unsigned int fnum [NR_SOUNDS];
int octave [NR_SOUNDS];
int volume [NR_SOUNDS];
int myVoice [NR_SOUNDS]; /* voice number being used */
int voices [NR_VOICES]; /* used to allocate voices */
int mode; /* card mode */
int start_time = 100;
int timing = 100;
/* These routines are redefined to clarify their appearance in the code. */
#define SetVoiceVolume(x,y) SetVoiceVolume(myVoice[x],y)
#define NoteOn(x) NoteOn(myVoice[x]);
#define NoteOff(x) NoteOff(myVoice[x]);
main( argc, argv)
int argc;
char *argv[];
{
int i;
extern SoundColdInit();
for ( i = 1, argv++; i < argc; i++, argv++)
{
strupr ( *argv);
if ( ! strncmp( "/T", *argv, 2)) start_time = atoi (*argv + 2);
}
Instrument_Init ();
Demo_Init ();
Choose_Sound (); /* user interface routine */
Stifle ();
SoundColdInit (0x388);
}
/*-----------------------------------------------------------------------*/
float frq_low [12] = {16.352, 17.324, 18.354, 19.445, 20.601, 21.826,
23.124, 24.499, 25.956, 27.500, 29.135, 30.867};
unsigned int freq_nums [12];
/*
frq_low is a table of frequencies for the first 12 pitches. If Note_A is
one octave higher than Note_B, then the frequency of Note_B is twice that
of Note_A. We can then use this relationship to find the frequency for any
note by multiplying the value in frq_low (pitch % 12) by a power of 2, where
the power of two is the octave (b = pitch / 12 - 1). This can be expressed
as follows:
frequency = frq_low [pitch % 12] * (2 ** b)
freq_nums is a table of f-numbers given a pitch (modulo 12). The card uses
the f-number and the octave bits to determine the pitch it will output.
Note that the f-numbers preserve the same power of 2 relationship between
octaves as for frequency.
The f-numbers can be calculated using a transformed version of the formula
on page 47 of the programmer's manual:
1. fnum = freq * (2 ** 20-b) / 50 kHz
2. fnum = freq * (2 ** 20) * (2 ** -b) / 50000 (from 1)
3. fnum = freq * 20.971 * (2 ** -b) (from 2)
4. freq = frq_low [pitch % 12] * (2 ** b)
5. fnum = frq_low [pitch % 12] * 20.971 (from 3 & 4)
(b is the octave less 1 so that 0 <= b <= 7.)
Thus, the f-numbers are the integer results of multiplying the values in
frq_low by 20.971.
This gives a range of 343 to 685 for the f-numbers. However, the f-number
register is 9 bits and can hold an unsigned value of 0 to 1023. The range
outside of 343-685 can be used, but it should be noted that in the lower
ranges, a small change in f-number will yield a larger change in output
frequency. By always using the range 343-685, one will have the same
incremental degree of control between pitches regardless of the octave.
*/
/*-----------------------------------------------------------------------
This sets up the table of f-numbers for a given pitch. This actually could
be calculated beforehand and the array initialized to the calculated values,
but it is done here for illustrative purposes. */
SetUp_FNums ()
{
int n;
float f;
for (n=0; n < 12; n++) {
f = frq_low [n] * 20.971;
freq_nums [n] = (unsigned int) f;
/* Round number up if necessary */
f -= (float) freq_nums [n];
if (f > 0.5) freq_nums [n]++;
}
}
/*-----------------------------------------------------------------------
This does a note-on for the passed f-num. If the note is already on,
only the frequency of the note will change. */
Out_Freq (sound_ID, fNbr, octave)
unsigned int sound_ID;
unsigned int fNbr; /* f-number for card */
unsigned int octave;
{
unsigned int t1;
octave -= 1;
while (fNbr > 1023) {
fNbr >>= 1;
octave++;
}
while (fNbr < freq_nums [0]) {
fNbr <<= 1;
octave--;
}
SndOutput (0xA0 + myVoice [sound_ID], fNbr);
t1 = 0x20 | (octave << 2) | (0x3 & (fNbr >> 8));
SndOutput (0xB0 + myVoice [sound_ID], t1);
return (octave);
}
/*-----------------------------------------------------------------------
Initialize the card and set up several variables. */
Demo_Init ()
{
extern SoundColdInit();
int n;
timing = start_time;
n = SoundColdInit (0x388);
if (!n) {
printf ("\nSound card not found.\n");
exit (0);
}
mode = MODE;
SetMode (mode);
SetUp_FNums ();
for (n=0; n < NR_VOICES; n++) voices [n] = -1;
for (n=0; n < NR_SOUNDS; n++) {
myVoice [n] = -1;
volume [n] = 0x50;
fnum [n] = 0;
}
}
/*-----------------------------------------------------------------------
Given a pitch, return its f-number. */
#define Pitch_to_Fnum(x) (freq_nums [x % 12])
/*
unsigned int Pitch_to_Fnum (pitch)
int pitch;
{
return (freq_nums [pitch % 12]);
}
*/
/*-----------------------------------------------------------------------
If the release values have been set to zero, a sound will continue to be
heard even if a note-off is done. This routine resets the release values
for every voice to an arbitrary value in order to shut them off. */
Stifle_Voice (sound_ID)
int sound_ID;
{
int voice;
voice = myVoice [sound_ID];
if (voice == -1) return;
SetASlotParam (slotVoice [voice][0], prmRelease, 10);
SetASlotParam (slotVoice [voice][1], prmRelease, 10);
NoteOff (sound_ID);
Free_Voice (sound_ID);
}
/* Silence all voices. */
Stifle ()
{
int n;
for (n=0; n < NR_SOUNDS; n++) Stifle_Voice (n);
}
/*-----------------------------------------------------------------------
Finds an available voice and sets the approriate entry in the myVoice table.
Returns 0 if no more voices available else returns 1.*/
Alloc_Voice (sound_ID)
int sound_ID;
{
register int n;
for (n=0; n < NR_VOICES && voices [n] != -1; n++);
if (n >= NR_VOICES) return (0);
voices [n] = sound_ID;
myVoice [sound_ID] = n;
return (1);
}
/* De-allocate the voice which was allocated in the above routine. */
Free_Voice (sound_ID)
int sound_ID;
{
register int n;
n = myVoice [sound_ID];
if (n == -1) return;
voices [n] = -1;
myVoice [sound_ID] = -1;
}
/*-----------------------------------------------------------------------
Read the .INS file into the appropriate operator buffers. */
Read_Ins_File (name, sound_ID)
char *name;
int sound_ID;
{
unsigned char *op0, *op1;
int file, n, ok;
unsigned char temp [20];
op0 = op [sound_ID][0];
op1 = op [sound_ID][1];
file = open (name, O_RDONLY & O_RAW);
if (file < 0) {
printf ("Can't open %s\n", name);
return (0);
}
read (file, temp, 2);
for (n=0; n < 13; n++) read (file, &op0 [n], 2);
for (n=0; n < 13; n++) read (file, &op1 [n], 2);
ok = read (file, temp, 20);
if (ok) {
read (file, &op0 [prmWaveSel], 2);
read (file, &op1 [prmWaveSel], 2);
}
close (file);
return (1);
}
/*-----------------------------------------------------------------------
Given the timbre parameters, initialize a voice. NOTE: The real voice
number is passed here, not the sound's ID code. */
SetUp_Timbre (voice, op0, op1)
int voice;
unsigned char *op0, *op1;
{
SetCharSlotParam (slotVoice [voice][0], op0, op0 [prmWaveSel]);
if (mode == MELODIC || voice <= BD)
SetCharSlotParam (slotVoice [voice][1], op1, op1 [prmWaveSel]);
}
/*-----------------------------------------------------------------------
Set up an instrument for the passed voice. Resets the release values
according to the passed flag. This routine is similar to SetVoiceTimbre()
in ADLIB.C. */
Set_Instrument (sound_ID, rel_flag)
int sound_ID, rel_flag;
{
int ok, voice, rel_value;
unsigned char *op0, *op1;
ok = Alloc_Voice (sound_ID);
if (!ok) {
printf ("No more voices available\n");
return (0);
}
voice = myVoice [sound_ID];
op0 = op [sound_ID][0];
op1 = op [sound_ID][1];
/* Setting the release values to zero has the effect of making the note
play forever. */
if (rel_flag) rel_value = 1;
else rel_value = 0;
op0 [prmRelease] = rel_value;
op1 [prmRelease] = rel_value;
SetUp_Timbre (voice, op0, op1);
SetVoiceVolume (sound_ID, 0x60);
return (1);
}
/*-----------------------------------------------------------------------
Read in all of the required .INS files. */
Instrument_Init ()
{
Read_Ins_File ("instr\\jet11a.ins", JETa);
Read_Ins_File ("instr\\jet11b.ins", JETb);
Read_Ins_File ("instr\\oboe1.ins", JETc);
Read_Ins_File ("instr\\siren2a.ins", JETd);
Read_Ins_File ("instr\\siren2a.ins", JETe);
Read_Ins_File ("instr\\flute.ins", BOMBa);
Read_Ins_File ("instr\\plane1.ins", BOMBb);
Read_Ins_File ("instr\\plane1.ins", BOMBc);
Read_Ins_File ("instr\\plane1.ins", BOMBd);
Read_Ins_File ("instr\\plane3.ins", PLANEa);
Read_Ins_File ("instr\\plane3.ins", PLANEb);
Read_Ins_File ("instr\\siren1.ins", SIREN);
Read_Ins_File ("instr\\helico3.ins", COPTER);
}
/*------------------------------------------------------------------------
This creates a delay in a rather simple manner in order to create a fake
timer. In a real application, a scheduling or a timer routine would have
call the Internal_Driver() routine at the appropriate moment. */
delay (n)
int n;
{
int i, j = 5;
for (i=0; i < (n * 100); i++) j %= 23;
}
/*********************************** Jet *********************************/
Drive_Jet ()
{
static int flag = 1;
static int n = -0x40;
if (!fnum [JETa]) return;
if (flag) {
/* raise the volume and pitch */
SetVoiceVolume (JETa, 0x50+n);
SetVoiceVolume (JETb, 0x60+n);
SetVoiceVolume (JETc, 0x30+(n/2));
SetVoiceVolume (JETd, 0x60+n);
SetVoiceVolume (JETe, 0x45+n);
Out_Freq (JETa, fnum [JETa]+n, octave [JETa]);
Out_Freq (JETb, fnum [JETb]+n, octave [JETb]);
Out_Freq (JETd, fnum [JETd]+n, octave [JETd]);
Out_Freq (JETe, fnum [JETe]+n, octave [JETe]);
n++;
if (n >= 0x1f) flag = 0;
}
else /* flag is 0 */ {
/* lower the volume and pitch */
SetVoiceVolume (JETa, 0x50+n);
SetVoiceVolume (JETb, 0x60+n);
SetVoiceVolume (JETc, 0x30+(n/2));
SetVoiceVolume (JETd, 0x60+n);
SetVoiceVolume (JETe, 0x45+n);
Out_Freq (JETa, fnum [JETa]+n, octave [JETa]);
Out_Freq (JETb, fnum [JETb]+n, octave [JETb]);
Out_Freq (JETd, fnum [JETd]+n, octave [JETd]);
Out_Freq (JETe, fnum [JETe]+n, octave [JETe]);
n--;
if (n <= -0x40) {
/* "shut off" the jet: set variables to zero */
flag = 1;
fnum [JETa] = 0;
fnum [JETb] = 0;
fnum [JETc] = 0;
fnum [JETd] = 0;
fnum [JETe] = 0;
Stifle_Voice (JETa);
Stifle_Voice (JETb);
Stifle_Voice (JETc);
Stifle_Voice (JETd);
Stifle_Voice (JETe);
}
}
}
/*-----------------------------------------------------------------------
This needs five voices to get a good effect. JET11A.INS and JET11B.INS
alone have been described as being like a vacuum cleaner. But if ever
you need a vacuum cleaner sound... */
Jet ()
{
int n, ok;
if (fnum [JETa]) return;
Set_Instrument (JETa, NO_RELEASE);
Set_Instrument (JETb, NO_RELEASE);
Set_Instrument (JETc, NO_RELEASE);
Set_Instrument (JETd, NO_RELEASE);
ok = Set_Instrument (JETe, NO_RELEASE);
if (!ok) {
for (n=JETa; n < JETe; n++) Free_Voice (n);
return;
}
fnum [JETa] = Pitch_to_Fnum (52);
fnum [JETb] = Pitch_to_Fnum (88);
fnum [JETc] = Pitch_to_Fnum (16);
fnum [JETd] = Pitch_to_Fnum (100);
fnum [JETe] = Pitch_to_Fnum (101);
octave [JETa] = 52 / 12;
octave [JETb] = 88 / 12;
octave [JETc] = 1;
octave [JETd] = 100 / 12;
octave [JETe] = 101 / 12;
/* The pitch of JETc does not vary, so it turned on on here. The other
voices are output in Drive_Jet(). */
SetVoiceVolume (JETc, 0);
Out_Freq (JETc, fnum [JETc], octave [JETc]);
Drive_Jet ();
printf ("Jet started\n");
}
/*********************************** Bomb ********************************/
unsigned bomb_low;
int wait_time;
Drive_Bomb ()
{
if (!fnum [BOMBa]) return;
if (fnum [BOMBa] > bomb_low) {
/* Decrease pitch of bomb falling sound */
fnum [BOMBa] -= 4;
Out_Freq (BOMBa, fnum [BOMBa], octave [BOMBa]);
}
else {
if (!wait_time) {
/* Shut off the flute (bomb falling sound) */
Stifle_Voice (BOMBa);
/* Turn on the exploding sound. The more voices which can be used
here, the better the effect, although the minimum is two voices.
For each voice added, set the pitch a half-tone above the
previous voice. */
Out_Freq (BOMBb, fnum [BOMBb], octave [BOMBb]);
Out_Freq (BOMBc, fnum [BOMBc], octave [BOMBc]);
Out_Freq (BOMBd, fnum [BOMBd], octave [BOMBd]);
wait_time = 160;
}
else { /* wait to let sound fade, otherwise it may get cut off */
wait_time--;
if (!wait_time) { /* wait has expired, free voices */
timing += 5;
fnum [BOMBa] = 0;
Stifle_Voice (BOMBb);
Stifle_Voice (BOMBc);
Stifle_Voice (BOMBd);
}
}
}
}
/*-----------------------------------------------------------------------*/
Bomb ()
{
int new_oct, ok;
unsigned char c;
if (!fnum [BOMBa]) {
Set_Instrument (BOMBb, RELEASE);
Set_Instrument (BOMBc, RELEASE);
Set_Instrument (BOMBd, RELEASE);
ok = Set_Instrument (BOMBa, NO_RELEASE);
if (!ok) {
Free_Voice (BOMBb);
Free_Voice (BOMBc);
Free_Voice (BOMBd);
return;
}
timing -= 5;
fnum [BOMBb] = Pitch_to_Fnum (12);
fnum [BOMBc] = Pitch_to_Fnum (13);
fnum [BOMBd] = Pitch_to_Fnum (14);
octave [BOMBb] = 1;
octave [BOMBc] = 1;
octave [BOMBd] = 1;
SetVoiceVolume (BOMBb, 0x7f);
SetVoiceVolume (BOMBc, 0x7f);
SetVoiceVolume (BOMBd, 0x7f);
SetVoiceVolume (BOMBa, volume [BOMBa]);
}
octave [BOMBa] = 95 / 12;
fnum [BOMBa] = Pitch_to_Fnum (95);
bomb_low = fnum [BOMBa] >> 2;
wait_time = 0;
Out_Freq (BOMBa, fnum [BOMBa], octave [BOMBa]);
/* Read input from keyboard */
printf ("\n Bomb: press spacebar to exit, 'S' to silence ");
do {
while (! kbhit ())
Internal_Driver ();
c = getch ();
if (c) {
c = toupper (c);
if (c == 'S') {
timing += 5;
Stifle_Voice (BOMBa);
Stifle_Voice (BOMBb);
Stifle_Voice (BOMBc);
Stifle_Voice (BOMBd);
fnum [BOMBa] = 0;
c = ' ';
}
}
else {
c = getch ();
}
}
while (c != ' ');
}
/********************************* Airplane ******************************/
Drive_Airplane ()
{
/* Here, one could adjust the pitch up and down slightly in order to
give the sound a more pronounced whine. */
}
/*-----------------------------------------------------------------------*/
Airplane ()
{
static int inc = 6;
static pitch = 30;
int n, new_oct, ok;
unsigned char c;
if (!fnum [PLANEb]) {
ok = Set_Instrument (PLANEa, NO_RELEASE);
if (!ok) return;
ok = Set_Instrument (PLANEb, NO_RELEASE);
if (!ok) {
Free_Voice (PLANEa);
return;
}
timing -= 5;
volume [PLANEb] = volume [PLANEa] * 3 / 4;
octave [PLANEa] = octave [PLANEb] = pitch / 12;
fnum [PLANEa] = Pitch_to_Fnum (pitch);
fnum [PLANEb] = fnum [PLANEa] + 2;
SetVoiceVolume (PLANEa, volume [PLANEa]);
SetVoiceVolume (PLANEb, volume [PLANEb]);
Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
}
/* Read keyboard input */
printf ("\nAirplane: press spacebar to exit, 'S' to silence ");
do {
while (! kbhit ())
Internal_Driver ();
c = getch ();
if (c) {
c = toupper (c);
if (c == 'S') {
timing += 5;
Stifle_Voice (PLANEa);
Stifle_Voice (PLANEb);
fnum [PLANEa] = 0;
fnum [PLANEb] = 0;
c = ' ';
}
}
else {
c = getch ();
switch (c) {
case LEFT:
/* Decrease volume */
if (volume [PLANEa] == 0) break;
volume [PLANEa] -= 2;
volume [PLANEb] = volume [PLANEa] * 3 / 4;
SetVoiceVolume (PLANEa, volume [PLANEa]);
SetVoiceVolume (PLANEb, volume [PLANEb]);
break;
case RIGHT:
/* Increase volume */
if (volume [PLANEa] >= 0x7e) break;
volume [PLANEa] += 2;
volume [PLANEb] = volume [PLANEa] * 3 / 4;
SetVoiceVolume (PLANEa, volume [PLANEa]);
SetVoiceVolume (PLANEb, volume [PLANEb]);
break;
case UP:
/* Increase pitch */
fnum [PLANEa] += 2;
Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
fnum [PLANEb] = fnum [PLANEa] + 1;
break;
case DOWN:
/* Decrease pitch */
if (fnum [PLANEa] <= 18) break;
fnum [PLANEa] -= 2;
Out_Freq (PLANEa, fnum [PLANEa], octave [PLANEa]);
fnum [PLANEb] = fnum [PLANEa] + 1;
break;
}
}
}
while (c != ' ');
}
/*********************************** Siren *******************************/
unsigned siren_high;
unsigned siren_low;
Drive_Siren () {
static flag = 1;
if (!fnum [SIREN]) return;
if (flag) {
/* increase pitch */
fnum [SIREN] += 4;
Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
if (fnum [SIREN] >= siren_high) flag = 0;
}
else {
/* decrease pitch */
fnum [SIREN] -= 4;
Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
if (fnum [SIREN] <= siren_low) flag = 1;
}
}
/*-----------------------------------------------------------------------*/
Siren ()
{
int ok;
unsigned char c;
if (!fnum [SIREN]) {
ok = Set_Instrument (SIREN, NO_RELEASE);
if (!ok) return;
timing -= 5;
volume [SIREN] = 0x60;
octave [SIREN] = 67 / 12;
fnum [SIREN] = Pitch_to_Fnum (67) >> 1;
siren_low = Pitch_to_Fnum (66);
siren_high = Pitch_to_Fnum (71);
SetVoiceVolume (SIREN, volume [SIREN]);
Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
}
/* Read keyboard input */
printf ("\nSiren: press spacebar to exit, 'S' to silence ");
do {
while (! kbhit ())
Internal_Driver ();
c = getch ();
if (c) {
c = toupper (c);
if (c == 'S') {
timing += 5;
Stifle_Voice (SIREN);
fnum [SIREN] = 0;
c = ' ';
}
}
else {
c = getch ();
switch (c) {
case LEFT:
/* decrease volume */
if (volume [SIREN] == 0) break;
volume [SIREN] -= 2;
SetVoiceVolume (SIREN, volume [SIREN]);
break;
case RIGHT:
/* increase volume */
if (volume [SIREN] == 0x7f) break;
volume [SIREN] += 2;
SetVoiceVolume (SIREN, volume [SIREN]);
break;
case UP:
/* increase pitch */
fnum [SIREN] += 8;
Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
break;
case DOWN:
/* decrease pitch */
fnum [SIREN] -= 8;
Out_Freq (SIREN, fnum [SIREN], octave [SIREN]);
break;
}
}
}
while (c != ' ');
}
/******************************** Helicopter *****************************/
int heli_wait;
Drive_Helicopter ()
{
static int cntr = 0;
if (!fnum [COPTER]) return;
cntr++;
if (cntr >= heli_wait) {
NoteOff (COPTER);
Out_Freq (COPTER, fnum [COPTER], octave [COPTER]);
cntr = 0;
}
}
/*-----------------------------------------------------------------------*/
Helicopter ()
{
int pitch, ok;
unsigned char c;
if (!fnum [COPTER]) {
ok = Set_Instrument (COPTER, RELEASE);
if (!ok) return;
timing -= 5;
heli_wait = 5;
pitch = 39;
volume [COPTER] = 0x70;
octave [COPTER] = pitch / 12;
fnum [COPTER] = Pitch_to_Fnum (pitch);
SetVoiceVolume (COPTER, volume [COPTER]);
Out_Freq (COPTER, fnum [COPTER], octave [COPTER]);
}
/* Read keyboard input */
printf ("\nHelicopter: Pg Up - faster, Pg Dn - slower, spacebar to exit ");
do {
while (! kbhit ()) Internal_Driver ();
c = getch ();
if (c) {
c = toupper (c);
if (c == 'S') {
timing += 5;
Stifle_Voice (COPTER);
fnum [COPTER] = 0;
c = ' ';
}
}
else {
c = getch ();
switch (c) {
case LEFT:
/* decrease volume */
if (volume [COPTER] == 0) break;
volume [COPTER] -= 2;
SetVoiceVolume (COPTER, volume [COPTER]);
break;
case RIGHT:
/* increase volume */
if (volume [COPTER] == 0x7f) break;
volume [COPTER] += 2;
SetVoiceVolume (COPTER, volume [COPTER]);
break;
case UP:
/* increase pitch */
fnum [COPTER] += 2;
break;
case DOWN:
/* decrease pitch */
if (fnum [COPTER] > 0) fnum [COPTER] -= 2;
break;
case PAGEUP:
/* increase frequency of note off/on */
if (heli_wait > 0) heli_wait--;
break;
case PAGEDOWN:
/* decrease frequency of note off/on */
if (heli_wait < 0x7fff) heli_wait++;
break;
}
}
}
while (c != ' ');
}
/************************************************************************
Routine which allows one to read in an instrument file and play with
the pitch and volume. */
Test ()
{
#ifdef TEST
char fname [40];
int pitch, octave, volume;
unsigned int fnum1;
unsigned char c;
printf ("\nFile name: ");
scanf ("%s", fname);
Read_Ins_File (fname, FAKE_ID);
if (!Set_Instrument (FAKE_ID, NO_RELEASE)) return;
printf ("Start pitch: ");
scanf ("%d", &pitch);
volume = 0x60;
octave = pitch / 12;
fnum1 = Pitch_to_Fnum (pitch);
SetVoiceVolume (FAKE_ID, volume);
Out_Freq (FAKE_ID, fnum1, octave);
printf ("\nTest: press spacebar to stop ");
do {
while (! kbhit ());
c = getch ();
if (!c) {
c = getch ();
switch (c) {
case LEFT:
/* decrease volume */
if (volume == 0) break;
volume -= 2;
SetVoiceVolume (FAKE_ID, volume);
break;
case RIGHT:
/* increase volume */
if (volume == 0x7f) break;
volume += 2;
SetVoiceVolume (FAKE_ID, volume);
break;
case UP:
/* increase pitch */
fnum1 += 5;
Out_Freq (FAKE_ID, fnum1, octave);
break;
case DOWN:
/* decrease pitch */
fnum1 -= 4;
Out_Freq (FAKE_ID, fnum1, octave);
break;
}
}
}
while (c != ' ');
printf (" fnum=%d, octave=%d, vol=%d ", fnum1, octave, volume);
Stifle ();
#endif
}
/************************************************************************
This routine must be called constantly (or at a constant interval) in order
to keep producing the sound effects. In a real application, this routine or
a variation of it would be hooked up to the timer interrupt directly or via
another routine. */
Internal_Driver ()
{
Drive_Siren ();
Drive_Airplane ();
Drive_Bomb ();
Drive_Helicopter ();
Drive_Jet ();
delay (timing);
}
/*-----------------------------------------------------------------------
Main loop. Displays menu and reads user's selection. */
Choose_Sound ()
{
unsigned char c;
printf (
"\nTo speed up or slow down, re-execute using /Txxx, where xxx is a"
"\nnumber. xxx is currently %d.", timing);
printf ("\n\nUse up and down arrows to change pitch. Use left and\n"
"right arrows to change volume.");
do {
printf ("\nChoose one:\n A)irplane\n B)omb\n "
"H)elicopter\n J)et\n S)iren\n R)eset all\n Q)uit\n");
while (! kbhit ()) Internal_Driver ();
c = getch ();
if (!c) getch ();
else {
c = toupper (c);
switch (c) {
case 'A': Airplane ();
break;
case 'B': Bomb ();
break;
case 'H': Helicopter ();
break;
case 'J': Jet ();
break;
case 'R': Stifle ();
Demo_Init ();
break;
case 'S': Siren ();
break;
case 'T': Test ();
break;
}
}
}
while (c != 'Q');
}